package cn.tx.redislock.service;

import cn.tx.redislock.mylock.DistributedLockFactory;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author jizenan
 * @date 2025/6/6
 * Description:
 */
@Service
@Slf4j
public class InventoryService {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Value("${server.port}")
    private String port;

    private Lock lock = new ReentrantLock();
    //private Lock redisDistributedLock = new RedisDistributedLock();

    @Autowired
    private DistributedLockFactory factory;

    @Autowired
    private Redisson redisson;

    /**
     * V9.0，引入redisson官网推荐RedLock算法实现类
     * */
    public String saleByRedisson() {
        String retMessage = "";

        RLock redissonLock = redisson.getLock("zzyyRedisLock");
        redissonLock.lock();
        try {
            //1 查询库存信息
            String result = stringRedisTemplate.opsForValue().get("inventory001");
            //2 判断库存是否足够
            Integer inventoryNumber = result == null ? 0 : Integer.parseInt(result);
            //3 扣减库存，每次减少一个
            if(inventoryNumber > 0){
                stringRedisTemplate.opsForValue().set("inventory001",String.valueOf(--inventoryNumber));
                retMessage = "成功卖出一个商品，库存剩余："+inventoryNumber;
                System.out.println(retMessage+"\t"+"服务端口号"+port);
            }else {
                retMessage = "商品卖完了.....";
            }
        } finally {
            //改进点，只能删除自己的key，不能删除别人的
            if(redissonLock.isLocked() && redissonLock.isHeldByCurrentThread()){
                redissonLock.unlock();
            }
        }
        return retMessage+"\t"+"服务端口号"+port;
    }

    /**
     * V8.0实现自动续期功能的完善，后台自定义扫描程序，如果规定时间内没有完成业务逻辑，会调用加钟自动续期脚本
     *
    public String sale() {
        String retMessage = "";
        Lock redisLock = factory.getDistributedLock("redis");
        redisLock.lock();
        try {
            //1 查询库存信息
            String result = stringRedisTemplate.opsForValue().get("inventory001");
            //2 判断库存是否足够
            Integer inventoryNumber = result == null ? 0 : Integer.parseInt(result);
            //3 扣减库存，每次减少一个
            if(inventoryNumber > 0){
                stringRedisTemplate.opsForValue().set("inventory001",String.valueOf(--inventoryNumber));
                retMessage = "成功卖出一个商品，库存剩余："+inventoryNumber;
                System.out.println(retMessage+"\t"+"服务端口号"+port);
                //暂停120s，演示自动续期功能
                try { TimeUnit.SECONDS.sleep(120);} catch (InterruptedException e) {e.printStackTrace();}
            }else {
                retMessage = "商品卖完了.....";
            }
        } finally {
            redisLock.unlock();
        }
        return retMessage+"\t"+"服务端口号"+port;
    }
     */

    /**
     *V7.1,可重入性测试？
     *
    public String sale() {
        String retMessage = "";
        Lock redisLock = factory.getDistributedLock("redis");
        redisLock.lock();
        try {
            //1 查询库存信息
            String result = stringRedisTemplate.opsForValue().get("inventory001");
            //2 判断库存是否足够
            Integer inventoryNumber = result == null ? 0 : Integer.parseInt(result);
            //3 扣减库存，每次减少一个
            if(inventoryNumber > 0){
                stringRedisTemplate.opsForValue().set("inventory001",String.valueOf(--inventoryNumber));
                retMessage = "成功卖出一个商品，库存剩余："+inventoryNumber;
                System.out.println(retMessage+"\t"+"服务端口号"+port);
                testReEntry();
            }else {
                retMessage = "商品卖完了.....";
            }
        } finally {
            redisLock.unlock();
        }
        return retMessage+"\t"+"服务端口号"+port;
    }

    private void testReEntry() {
        Lock redisLock = factory.getDistributedLock("redis");
        redisLock.lock();
        try {
            System.out.println("====测试可重入锁============");
        } finally {
            redisLock.unlock();
        }
    }

     */

    /**
     *V7.0,如何将lock/unlock+lua脚本自研版的redis分布式锁搞定？
     *
    public String sale() {
        String retMessage = "";
        Lock redisLock = factory.getDistributedLock("redis");
        redisLock.lock();
        try {
            //1 查询库存信息
            String result = stringRedisTemplate.opsForValue().get("inventory001");
            //2 判断库存是否足够
            Integer inventoryNumber = result == null ? 0 : Integer.parseInt(result);
            //3 扣减库存，每次减少一个
            if(inventoryNumber > 0){
                stringRedisTemplate.opsForValue().set("inventory001",String.valueOf(--inventoryNumber));
                retMessage = "成功卖出一个商品，库存剩余："+inventoryNumber;
                System.out.println(retMessage+"\t"+"服务端口号"+port);
            }else {
                retMessage = "商品卖完了.....";
            }
        } finally {
            redisLock.unlock();
        }
        return retMessage+"\t"+"服务端口号"+port;
    }
     */

    /**
     * V6.0不满足可重入性，修改成7.0版
     *
    public String sale() {
        String retMessage = "";
        String key = "zzyy";
        String uuidValue = IdUtil.simpleUUID()+":"+Thread.currentThread().getId();
        //不用递归，用自旋，用while来代替
        while (!stringRedisTemplate.opsForValue().setIfAbsent(key, uuidValue,30L, TimeUnit.SECONDS)) {
            //暂停20毫秒，进行递归重试.....
            try { TimeUnit.MILLISECONDS.sleep(20);} catch (InterruptedException e) {e.printStackTrace();}
        }
        //抢锁成功的请求线程进行正常的业务逻辑，扣减库存
        try {
            //1 查询库存信息
            String result = stringRedisTemplate.opsForValue().get("inventory001");
            //2 判断库存是否足够
            Integer inventoryNumber = result == null ? 0 : Integer.parseInt(result);
            //3 扣减库存，每次减少一个
            if(inventoryNumber > 0){
                stringRedisTemplate.opsForValue().set("inventory001",String.valueOf(--inventoryNumber));
                retMessage = "成功卖出一个商品，库存剩余："+inventoryNumber;
                System.out.println(retMessage+"\t"+"服务端口号"+port);
            }else {
                retMessage = "商品卖完了.....";
            }
        } finally {
            //使用lua删除
            String luaScript =
                    "if redis.call('get',KEYS[1]) == ARGV[1] then " +
                            "return redis.call('del',KEYS[1]) " +
                            "else " +
                            "return 0 " +
                            "end";
            stringRedisTemplate.execute(new DefaultRedisScript<>(luaScript,Boolean.class), Arrays.asList(key),uuidValue);
        }
        return retMessage+"\t"+"服务端口号"+port;
    }
     */

    /*
    * v3.1版递归重试
    public String sale() {
        String retMessage = "";
        String key = "zzyy";
        String uuidValue = IdUtil.simpleUUID()+":"+Thread.currentThread().getId();
        Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, uuidValue);
        //flag=false,抢不到的线程要继续重试。。。。。
        if(!flag){
            //暂停20毫秒，进行递归重试.....
            try { TimeUnit.MILLISECONDS.sleep(20);} catch (InterruptedException e) {e.printStackTrace();}
            sale();
        }else {
            //抢锁成功的请求线程进行正常的业务逻辑，扣减库存
            try {
                //1 查询库存信息
                String result = stringRedisTemplate.opsForValue().get("inventory001");
                //2 判断库存是否足够
                Integer inventoryNumber = result == null ? 0 : Integer.parseInt(result);
                //3 扣减库存，每次减少一个
                if(inventoryNumber > 0){
                    stringRedisTemplate.opsForValue().set("inventory001",String.valueOf(--inventoryNumber));
                    retMessage = "成功卖出一个商品，库存剩余："+inventoryNumber;
                    System.out.println(retMessage+"\t"+"服务端口号"+port);
                }else {
                    retMessage = "商品卖完了.....";
                }
            } finally {
                stringRedisTemplate.delete(key);
            }
        }
        return retMessage+"\t"+"服务端口号"+port;
    }
     */

    /*
    * V2.0,单机版加锁配合Nginx和Jmeter压测后，不满足高并发分布式锁的性能要求，出现超卖
    *
    public String sale() {
        String retMessage = "";
        lock.lock();
        try {
            //1 查询库存信息
            String result = stringRedisTemplate.opsForValue().get("inventory001");
            //2 判断库存是否足够
            Integer inventoryNumber = result == null ? 0 : Integer.parseInt(result);
            //3 扣减库存，每次减少一个
            if(inventoryNumber > 0){
                stringRedisTemplate.opsForValue().set("inventory001",String.valueOf(--inventoryNumber));
                retMessage = "成功卖出一个商品，库存剩余："+inventoryNumber;
                System.out.println(retMessage+"\t"+"服务端口号"+port);
            }else {
                retMessage = "商品卖完了.....";
            }
        } finally {
            lock.unlock();
        }
        return retMessage+"\t"+"服务端口号"+port;
    }
     */
}
